/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

#include "stdafx.h"
#include "DConfigGlobals.h"
#include "TSocket.h"

///////////////////////////////////
#if defined(USING_MFC) && defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////

#if defined(__WINNT__) || defined(__WINCE__)
	long TSocket::m_InstanceCount = 0;
	WSADATA TSocket::m_wsaData;
#	define SOCKERRNO	WSAGetLastError()
#else
#	include <errno.h>
#	include <time.h>
#	include <sys/time.h>
#	include <sys/types.h>
#	define SOCKERRNO	errno
#endif

#ifdef __WINCE__
#	define SOCKVERSION	MAKEWORD(2, 2)
#else if defined(__WINNT__)
#	define SOCKVERSION	MAKEWORD(2, 0)
#endif


#define MAX_PACKET_SIZE		1024	// bytes

////////////////////////////////////////////////

TSocket::TSocket()
{
	m_LastErrorType = seNone;
	m_Socket = INVALID_SOCKET;
	m_bConnected = false;
	m_TimeOut = 10000;		// Default to 10 seconds before timeout
	m_Terminator = 0;
	m_bUseTerminator = false;
	m_RecvStart = 0;
	memset(m_HostName, 0, sizeof(m_HostName));

#if defined(__WINNT__) || defined(__WINCE__)
	InterlockedIncrement(&m_InstanceCount);
	if (m_InstanceCount == 1) {
		// Only need to call WSAStartup once
		if (WSAStartup(SOCKVERSION, &m_wsaData)) {
			// Don't use the virtual error function for this...
			RunTimeError(_T("Failed to init winsock"));
		}
	}
#endif
}

TSocket::TSocket(const TSocket& copy)
{
	if (this != &copy) {
		m_LastErrorType = copy.m_LastErrorType;
		m_Socket = copy.m_Socket;
		m_bConnected = copy.m_bConnected;
		m_TimeOut = copy.m_TimeOut;
		m_Terminator = copy.m_Terminator;
		m_bUseTerminator = copy.m_bUseTerminator;
		m_Port = copy.m_Port;
		m_AF = copy.m_AF;
		m_Type = copy.m_Type;
		m_sockAddr = copy.m_sockAddr;
#ifdef AF_IRDA
		memcpy(&m_irdaSockAddr, &copy.m_irdaSockAddr, sizeof(m_irdaSockAddr));
#endif
		_tcscpy(m_HostName, copy.m_HostName);
		m_RecvStart = 0;
		m_RecvCount = 0;
	}

#if defined(__WINNT__) || defined(__WINCE__)
	InterlockedIncrement(&m_InstanceCount);
	if (m_InstanceCount == 1) {
		// Only need to call WSAStartup once
		if (WSAStartup(SOCKVERSION, &m_wsaData)) {
			// Don't use the virtual error function for this...
			MessageBox(NULL, _T("Failed to init winsock"), _T("Error"), MB_OK);
		}
	}
#endif
}

TSocket::~TSocket()
{
	Close();

	if (m_Socket != INVALID_SOCKET) closesocket(m_Socket); // CE bind trouble

#if defined(__WINNT__) || defined(__WINCE__)
	InterlockedDecrement(&m_InstanceCount);
	if (m_InstanceCount == 0) {
		// WSACleanup() needs to be called once for each call to WSAStartup()
		WSACleanup();
	}
#endif
}

void TSocket::Error(socketErrors_t type, TString message)
{
	// virtual function so derived classes can implement different behaviour
	m_LastErrorType = type;
	m_LastErrorMsg = message;
	//ERROR_MSG(_T("Socket Error"), (LPCTSTR)message);
}

bool TSocket::WasError()
{
	return m_LastErrorType != seNone;
}

SOCKET TSocket::fileno()
{
	return m_Socket;
}

TString TSocket::GetIP()
{
	return m_HostName;
}

TString TSocket::GetHostName()
{
	#if defined(__WINNT__) || defined(__WINCE__)
		// This is a static func, constructor may not have been
		// called previously in which case we need to init winsock
		// else we'll fail.
		InterlockedIncrement(&m_InstanceCount);
		if (m_InstanceCount == 1) {
			// Only need to call WSAStartup once
			if (WSAStartup(SOCKVERSION, &m_wsaData)) {
				// Don't use the virtual error function for this...
				MessageBox(NULL, _T("Failed to init winsock"), _T("Error"), MB_OK);
			}
		}
	#endif

	TString result;
	char buf[128];
	int code = gethostname(buf, 128);
	if (code != 0) buf[0] = 0;

	#ifdef __WINCE__
		char* p = buf;
		while (*p) result += *p++;
	#else
		result = buf;
	#endif

	#if defined(__WINNT__) || defined(__WINCE__)
		// Release winsock, if we were the reason it was loaded...
		InterlockedDecrement(&m_InstanceCount);
		if (m_InstanceCount == 0) {
			// WSACleanup() needs to be called once for each call to WSAStartup()
			WSACleanup();
		}
	#endif
	return result;
}

void TSocket::SetTimeOut(DWORD timeOut)
{
	// Time out of zero means block with no timeout
	m_TimeOut = timeOut;
}

int TSocket::GetTimeOut() {return m_TimeOut;}

void TSocket::SetTerminator()
{
	m_bUseTerminator = 0;
}

void TSocket::SetTerminator(char termChar)
{
	m_bUseTerminator = 1;	// One term char
	m_Terminator = termChar;
}

void TSocket::SetTerminator2(char termChar, char termChar2)
{
	m_bUseTerminator = 2;	// two term chars
	m_Terminator = termChar;
	m_Terminator2 = termChar2;
}

bool TSocket::GetTerminator(TString& term)
{
	if (!m_bUseTerminator) {
		term.Empty();
		return false;
	}
	term = m_Terminator;
	if (m_bUseTerminator == 2) {
		term += m_Terminator2;
	}
	return true;
}

/*bool TSocket::GetTerminator(char& term)
{
	if (!m_bUseTerminator) {
		term = 0;
		return false;
	}
	term = m_Terminator;
	return true;
}*/

void TSocket::Close()
{
	if (m_bConnected) {
		int x = shutdown(m_Socket, 2);
		ASSERT(x != SOCKET_ERROR);
#if defined(__WINNT__) || defined(__WINCE__)
		closesocket(m_Socket);
#else
		close(m_Socket);
#endif
		m_bConnected = false;
		m_Socket = INVALID_SOCKET;
		m_RecvStart = 0;
	}
}

bool TSocket::CreateSocket(int af, int type, int protocol)
{
	if (m_bConnected) Close();

	m_AF = af;
	m_Type = type;
	m_Socket = socket(af, type, protocol);
	//m_Socket = socket(AF_IRDA, SOCK_STREAM, 0);

	if (m_Socket == INVALID_SOCKET) {
		Error(seSocket, _T("Failed to create socket: ") + SockErrorStr(SOCKERRNO));
		return false;
	}
	return true;
}

VALUE TSocket::GetAllHostByName(TString hostName)
{
	VALUE vResult = AllocArray(0);
	#if defined(__WINNT__) || defined(__WINCE__)
		// This is a static func, constructor may not have been
		// called previously in which case we need to init winsock
		// else we'll fail.
		InterlockedIncrement(&m_InstanceCount);
		if (m_InstanceCount == 1) {
			// Only need to call WSAStartup once
			if (WSAStartup(SOCKVERSION, &m_wsaData)) {
				// Don't use the virtual error function for this...
				MessageBox(NULL, _T("Failed to init winsock"), _T("Error"), MB_OK);
			}
		}
	#endif

	#ifdef __WINCE__
		int length = hostName.GetLength();
		char* asciiBuf = new char[length + 1];
		LPCTSTR name = hostName;
		for (int idx = 0; idx < length; ++idx) {
			asciiBuf[idx] = (char)name[idx];
		}
		asciiBuf[length] = 0;
		LPHOSTENT lpHostEnt = ::gethostbyname(asciiBuf);
		delete[] asciiBuf;
	#else
		LPHOSTENT lpHostEnt = ::gethostbyname((LPCTSTR)hostName);
	#endif

	#if defined(__WINNT__) || defined(__WINCE__)
		// Release winsock, if we were the reason it was loaded...
		InterlockedDecrement(&m_InstanceCount);
		if (m_InstanceCount == 0) {
			// WSACleanup() needs to be called once for each call to WSAStartup()
			WSACleanup();
		}
	#endif

	if (lpHostEnt != NULL) {
		TString str, addr;
		for(int i = 0; lpHostEnt->h_addr_list[i] != NULL; i++) {
 			for(int j = 0; j < lpHostEnt->h_length; j++ ) {
 				if( j > 0 )	str += ".";
 				addr.Format(_T("%u"), (unsigned int)((unsigned char*)lpHostEnt->h_addr_list[i])[j]);
				str += addr;
 			}
  			// str now contains one local IP address
			vResult.Resize(vResult.Length() + 1, str);
			str.Empty();
		}
	}
	return vResult;
}

TString TSocket::GetHostByName(TString hostName)
{
#if defined(__WINNT__) || defined(__WINCE__)
	// This is a static func, constructor may not have been
	// called previously in which case we need to init winsock
	// else we'll fail.
	InterlockedIncrement(&m_InstanceCount);
	if (m_InstanceCount == 1) {
		// Only need to call WSAStartup once
		if (WSAStartup(SOCKVERSION, &m_wsaData)) {
			// Don't use the virtual error function for this...
			MessageBox(NULL, _T("Failed to init winsock"), _T("Error"), MB_OK);
		}
	}
#endif

#ifdef __WINCE__
	int length = hostName.GetLength();
	char* asciiBuf = new char[length + 1];
	LPCTSTR name = hostName;
	for (int idx = 0; idx < length; ++idx) {
		asciiBuf[idx] = (char)name[idx];
	}
	asciiBuf[length] = 0;
	LPHOSTENT lpHostEnt = ::gethostbyname(asciiBuf);
	delete[] asciiBuf;
#else
	LPHOSTENT lpHostEnt = ::gethostbyname((LPCTSTR)hostName);
#endif

#if defined(__WINNT__) || defined(__WINCE__)
	// Release winsock, if we were the reason it was loaded...
	InterlockedDecrement(&m_InstanceCount);
	if (m_InstanceCount == 0) {
		// WSACleanup() needs to be called once for each call to WSAStartup()
		WSACleanup();
	}
#endif

	if (lpHostEnt == NULL) {
		return _T("");
	}
#ifdef __WINCE__
	char *pszAddress = inet_ntoa(*(PIN_ADDR)(lpHostEnt->h_addr_list[0]));
	TString temp;
	while(*pszAddress) {
		temp += *pszAddress++;
	}
	return temp;
#else
	return (LPCTSTR)inet_ntoa(*(PIN_ADDR)(lpHostEnt->h_addr_list[0]));
#endif
}

bool TSocket::GenerateSockAddr()
{
	if (*m_HostName == '\0') {
		m_sockAddr.sin_family = m_AF;
		m_sockAddr.sin_port = htons(m_Port);
		m_sockAddr.sin_addr.s_addr = INADDR_ANY;
		// Possibly not quite the right way to do this...
		TString ip = GetHostByName(GetHostName());
		for (int idx = 0; idx < ip.GetLength(); ++idx) {
			m_HostName[idx] = (char)(ip[idx]);
		}
		m_HostName[ip.GetLength()] = '\0';
		return true;
	}

#ifdef AF_IRDA
	if (m_AF == AF_IRDA) {
		// We are using infrared
		// Fill in SOCKADDR_IRDA structure
		memset(&m_irdaSockAddr, 0, sizeof(SOCKADDR_IRDA));
		m_irdaSockAddr.irdaAddressFamily = AF_IRDA;
		int idx = 0;
		while (m_HostName[idx]) {
			if (idx == 24) {
				Error(seSocket, _T("Hostname exceeded IRDA storage buffer"));
				return false;
			}
			m_irdaSockAddr.irdaServiceName[idx] = (char)(m_HostName[idx]);
			++idx;
		}
		return true;
	}
#endif // AF_IRDA

	// Not using IRDA...

	LPHOSTENT m_lpHostEnt;

	IN_ADDR inaddr;
	bool bUseInaddr = false;

#ifdef UNICODE
	// Hack convert from unicode host name to ascii (required by gethostbyname())
	char buf[128];
	int idx = 0;
	while(m_HostName[idx] != '\0') {
		buf[idx] = (char)(m_HostName[idx]);
		++idx;
	}
	buf[idx] = '\0';
	inaddr.S_un.S_addr = inet_addr(buf);
	if (inaddr.S_un.S_addr != INADDR_NONE) {
		bUseInaddr = true;
	}
	else {
		m_lpHostEnt = gethostbyname(buf);
	}
#elif defined(__WINNT__)
	inaddr.S_un.S_addr = inet_addr(m_HostName);
	if (inaddr.S_un.S_addr != INADDR_NONE) {
		bUseInaddr = true;
	}
	else {
		m_lpHostEnt = gethostbyname(m_HostName);
	}
#else
	if (inet_aton(m_HostName, &inaddr)) {
		bUseInaddr = true;
	}
	else {
		m_lpHostEnt = gethostbyname(m_HostName);
	}
#endif
	if (!bUseInaddr && (m_lpHostEnt == NULL)) {
		Error(seSocket, _T("Failed to gethostbyname"));
		return false;
	}

	m_sockAddr.sin_family = m_AF;
	m_sockAddr.sin_port = htons(m_Port);
	if (bUseInaddr) {
		m_sockAddr.sin_addr = inaddr;
	}
	else {
		m_sockAddr.sin_addr = *((PIN_ADDR)*m_lpHostEnt->h_addr_list);
	}
	return true;
}

bool TSocket::Connect(LPCTSTR hostName, int port)
{
	if (m_Socket == INVALID_SOCKET) {
		Error(seSocket, _T("Can't connect, socket is invalid"));
		return false;
	}

	if (m_bConnected) Close();

	int length = _tcslen(hostName);
	if (length >= 128) {
		Error(seSocket, _T("Hostname exceeded storage buffer"));
		return false;
	}
	_tcscpy(m_HostName, hostName);
	m_Port = port;

	if (!GenerateSockAddr()) return false;

	int nConnect;

#ifdef AF_IRDA
	if (m_AF == AF_IRDA) {
		// We are using infrared
		DEVICELIST devList;
		int devListLen = sizeof(devList);
		int cnt = 0, NumRetries = 5;
		devList.numDevice = 0;
		while ((devList.numDevice == 0) && (cnt <= NumRetries)) {
			getsockopt(m_Socket, SOL_IRLMP, IRLMP_ENUMDEVICES,
				(char*)&devList, &devListLen);
			cnt++;
			Sleep(1000);
		}
		if (cnt > NumRetries) {
			Error(seSocket, _T("Unable to locate IRDA server"));
			return false;
		}

		for (int idx = 0; idx <= 3; ++idx) {
			m_irdaSockAddr.irdaDeviceID[idx] = devList.Device[0].irdaDeviceID[idx];
		}
		nConnect = connect(m_Socket, (PSOCKADDR)&m_irdaSockAddr, sizeof(SOCKADDR_IRDA));
	}
	else {
		//nConnect = connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
		if (m_TimeOut == 0) {
			// Block until we get something...
			nConnect = connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
		}
		else {
			struct timeval timeout;
			timeout.tv_sec = m_TimeOut / 1000;
			timeout.tv_usec = (m_TimeOut % 1000) * 1000;

			fd_set writefds;
			FD_ZERO(&writefds);
			FD_SET(m_Socket, &writefds);

			// Set socket to non-blocking mode first
			unsigned long argp = 1;
			if (ioctlsocket(m_Socket, FIONBIO, &argp) == SOCKET_ERROR) {
				Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
				return false;
			}
			connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));

			int selectResult = select(m_Socket + 1, NULL, &writefds, NULL, &timeout);
			if (selectResult == SOCKET_ERROR) {
				closesocket(m_Socket);
				Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
				return false;
			}
			else if (selectResult != 0) {
				// Set socket to back to blocking mode
				argp = 0;
				if (ioctlsocket(m_Socket, FIONBIO, &argp) == SOCKET_ERROR) {
					closesocket(m_Socket);
					Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
					return false;
				}
				nConnect = 0; // Success
			}
			else {
				closesocket(m_Socket);
				Error(seTimeOut, _T("Socket connect timed out"));	// Timed out
				return false;
			}
		}
	}
#else
	//nConnect = connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
	if (m_TimeOut == 0) {
		// Block until we get something...
		nConnect = connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
	}
	else {
		struct timeval timeout;
		timeout.tv_sec = m_TimeOut / 1000;
		timeout.tv_usec = (m_TimeOut % 1000) * 1000;

		fd_set writefds;
		FD_ZERO(&writefds);
		FD_SET(m_Socket, &writefds);

		// Set socket to non-blocking mode first
		unsigned long argp = 1;
		if (ioctlsocket(m_Socket, FIONBIO, &argp) == SOCKET_ERROR) {
			Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
			return false;
		}
		connect(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));

		int selectResult = select(m_Socket + 1, NULL, &writefds, NULL, &timeout);
		if (selectResult == SOCKET_ERROR) {
			closesocket(m_Socket);
			Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
			return false;
		}
		else if (selectResult != 0) {
			// Set socket to back to blocking mode
			argp = 0;
			if (ioctlsocket(m_Socket, FIONBIO, &argp) == SOCKET_ERROR) {
				closesocket(m_Socket);
				Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
				return false;
			}
			nConnect = 0; // Success
		}
		else {
			closesocket(m_Socket);
			Error(seTimeOut, _T("Socket connect timed out"));	// Timed out
			return false;
		}
	}
#endif

	if (nConnect != 0) {
		Error(seSocket, (LPCTSTR)(SockErrorStr(nConnect) + _T(" : Failed to connect")));
		return false;
	}

	m_bConnected = true;
	return true;
}

bool TSocket::Bind(LPCTSTR hostName, int port)
{
	if (m_Socket == INVALID_SOCKET) {
		Error(seSocket, _T("Can't bind, socket is invalid"));
		return false;
	}

	if (m_bConnected) Close();

	if (hostName == (LPCTSTR)INADDR_ANY) {
		memset(m_HostName, 0, 128);
	}
	else {
		int length = _tcslen(hostName);
		if (length >= 128) {
			Error(seSocket, _T("Hostname exceeded storage buffer"));
			return false;
		}
		_tcscpy(m_HostName, hostName);
	}
	m_Port = port;
	if (!GenerateSockAddr()) return false;

	int result;
#ifdef AF_IRDA
	if (m_AF == AF_IRDA) {
		result = bind(m_Socket, (PSOCKADDR)&m_irdaSockAddr, sizeof(m_irdaSockAddr));
	}
	else {
		result = bind(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
	}
#else
	result = bind(m_Socket, (PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
#endif
	if (result == SOCKET_ERROR) {
		Error(seSocket, _T("socket.bind failed: ") + SockErrorStr(SOCKERRNO));
		return false;
	}
	return true;
}

bool TSocket::Listen(int backlog)
{
	if (listen(m_Socket, backlog) == SOCKET_ERROR) {
		Error(seSocket, _T("socket.listen failed"));
		return false;
	}
	return true;
}

TSocket* TSocket::Accept(TString& whoConnected)
{
	TSocket* s = new TSocket(*this);

#ifdef AF_IRDA
	if (m_AF == AF_IRDA) {
		s->m_Socket = accept(m_Socket, NULL, NULL);
		if (s->m_Socket == INVALID_SOCKET) goto AcceptFail;
		else whoConnected = _T("irda");//irdaSockAddr.irdaServiceName;
	}
	else {
		int sizeSockAddr = sizeof(SOCKADDR_IN);
		SOCKADDR_IN sockAddr;
		s->m_Socket = accept(m_Socket, (PSOCKADDR)&sockAddr, &sizeSockAddr);
		if (s->m_Socket == INVALID_SOCKET) goto AcceptFail;
		else whoConnected = inet_ntoa(sockAddr.sin_addr);
	}
#else
	int sizeSockAddr = sizeof(SOCKADDR_IN);
	SOCKADDR_IN sockAddr;
	s->m_Socket = accept(m_Socket, (PSOCKADDR)&sockAddr, &sizeSockAddr);
	if (s->m_Socket == INVALID_SOCKET) goto AcceptFail;
	else whoConnected = inet_ntoa(sockAddr.sin_addr);
#endif

	s->m_bConnected = true;
	_tcscpy(s->m_HostName, (LPCTSTR)whoConnected);
	return s;

AcceptFail:
	delete s;
	Error(seSocket, _T("socket.accept failed: ") + SockErrorStr(WSAGetLastError()));
	return NULL;
}

int TSocket::Send(const char* sendBuf, int sizeSendBuf)
{
	// Returns SOCKET_ERROR on failure, otherwise number of chars sent
	if (m_bConnected) {
		if (sizeSendBuf <= MAX_PACKET_SIZE) {
			int sent = send(m_Socket, sendBuf, sizeSendBuf, 0);
			if (sent == SOCKET_ERROR) {
				Error(seSocket, _T("Error sending socket data. ") + SockErrorStr(WSAGetLastError()));
			}
			else return sent;
		}
		else {
			int start = 0, bytesSent = 0, length;
			while (start < sizeSendBuf)	{
				length = ((sizeSendBuf - start) < MAX_PACKET_SIZE) ? (sizeSendBuf - start) : MAX_PACKET_SIZE;
				bytesSent = send(m_Socket, sendBuf + start, length, 0);
				start += bytesSent;
				if (bytesSent == SOCKET_ERROR) {
					Error(seSocket, _T("Socket::Send failed. ") + SockErrorStr(WSAGetLastError()));
					return SOCKET_ERROR;
				}
			}
			return start; // == total bytes sent (should == sizeSendBuf as well!)
		}
	}
	else {
		Error(seSocket, _T("Can't send until socket is connected"));
	}
	return SOCKET_ERROR;
}

int TSocket::Receive(char* recvBuf, int sizeRecvBuf)
{
	// Returns SOCKET_ERROR on failure, otherwise number of chars returned
	int offset = 0;

	if (m_RecvStart != 0) {
		// We have some bytes in m_RecvBuf from the TString version of
		// this function.  The left overs are from a terminator thing.
		// Calling this function should return those bytes first.
		int numBytes = m_RecvCount - m_RecvStart;
		if (sizeRecvBuf <= numBytes) {
			// More bytes in m_RecvBuf than needed, no need to call actual socket
			memcpy(recvBuf, m_RecvBuf + m_RecvStart, sizeRecvBuf);
			if (sizeRecvBuf == numBytes) {
				m_RecvStart = 0;
				m_RecvCount = 0;
			}
			else {
				m_RecvStart += sizeRecvBuf;
			}
			return sizeRecvBuf;
		}
		else {
			// Copy in bytes from previous read
			memcpy(recvBuf, m_RecvBuf + m_RecvStart, numBytes);
			offset = numBytes; // Store buffer offset for below
			m_RecvStart = 0;
			m_RecvCount = 0;
			// Are there any bytes in the actual OS socket buffer?
			// If yes, read some more.  If no, return what we already have so we don't block.
			DWORD bytesReady;
			if (ioctlsocket(m_Socket, FIONREAD, &bytesReady) == 0) {
				if (bytesReady == 0) {
					return numBytes;
				}
			}
		}
	}

	if (m_bConnected) {
		if (m_TimeOut == 0) {
			// Block until we get something...
			return recv(m_Socket, recvBuf, sizeRecvBuf, 0);
		}
		else {
			struct timeval timeout;
			timeout.tv_sec = m_TimeOut / 1000;
			timeout.tv_usec = (m_TimeOut % 1000) * 1000;

			fd_set readfds;
			FD_ZERO(&readfds);
			FD_SET(m_Socket, &readfds);

			int selectResult = select(m_Socket + 1, &readfds, NULL, NULL, &timeout);
			if (selectResult == SOCKET_ERROR) {
				Error(seSocket, _T("internal select failed: ") + SockErrorStr(SOCKERRNO));
			}
			else if (selectResult != 0) {
				int result = recv(m_Socket, recvBuf + offset, sizeRecvBuf - offset, 0) + offset;
				if (result == SOCKET_ERROR) {
					#if defined (__WINNT__) || defined (__WINCE__)
						int e = WSAGetLastError();
						if ((e == WSAENETRESET ) || ( e == WSAECONNABORTED ) ||
							(e == WSAECONNRESET )/* || (e == WSAEINVAL )*/)
						{
							return 0; // Connection closed, but not gracefully
						}
					#endif
					Error(seSocket, _T("socket receive failed: ") + SockErrorStr(SOCKERRNO));
				}
				return result;
			}
			else {
				// Timed out
				if (offset > 0) {
					return offset; // Return what was in previous buffer
				}
				else {
					memset(recvBuf, 0, sizeRecvBuf);
					Error(seTimeOut, _T("Socket read timed out"));
				}
			}
		}
	}
	else {
		Error(seSocket, _T("Can't receive until socket is connected"));
	}
	return SOCKET_ERROR;
}

/*void TSocket::Send(TString text)
{
	int i;
	int k = text.GetLength();
	char* buf = new char[k + 1];
#ifdef __WINCE__
	for(i=0;i<k;i++)
		buf[i]=(char) (text[i]);
#else
	strncpy(buf,(LPCTSTR)text, k);
#endif
	buf[k] = m_Terminator;
	int start = 0;
	int end = m_bUseTerminator ? k + 1 : k;
	while (start < end)
	{
		int length;
		length=((end-start)<512) ? (end-start) : 512;
		i = Send(buf + start, length);
		start += length;
		if (i == SOCKET_ERROR) {
			Error(seSocket, _T("TSocket::Send received a SOCKET_ERROR"));
			break; // throw exception
		}
	}
	delete[] buf;
}*/

void TSocket::Send(TString text)
{
	int i;
	int k = text.GetLength();
	char* buf = new char[k + m_bUseTerminator + 1];
	if (buf == NULL) {
		Error(seSocket, _T("TSocket::Send failed to allocate buffer"));
		return;
	}
#ifdef __WINCE__
	for(i=0;i<k;i++)
		buf[i]=(char)(text[i]);
#else
	strncpy(buf,(LPCTSTR)text, k);
#endif
	if (m_bUseTerminator >= 1) buf[k] = m_Terminator;
	if (m_bUseTerminator >= 2) buf[k + 1] = m_Terminator2;
	buf[k + m_bUseTerminator] = 0;

	int start = 0;
	int end = k + m_bUseTerminator;
	while (start < end)
	{
		int length;
		length=((end-start)<512) ? (end-start) : 512;
		i = TSocket::Send(buf + start, length);
		start += length;
		if (i == SOCKET_ERROR) {
			//Error(seSocket, _T("TSocket::Send received a SOCKET_ERROR"));
			// The error message was already set by the other Send()
			break; // throw exception
		}
	}
	delete[] buf;
}

int TSocket::Receive(TString& result)
{
	//char m_ReceiveBuf[RECVBUF_SIZE];
	//int m_ReceiveCount;
	//int m_ReceiveStart;

	result.Empty();
	while(true)	{
		if (m_RecvStart == 0) {
			m_RecvCount = TSocket::Receive(m_RecvBuf, RECVBUF_SIZE - 1);
			if (m_RecvCount == SOCKET_ERROR) {
				if (!m_bUseTerminator && !result.IsEmpty()) {
					return SOCKETRECV_OK; // There was an error, but we have data
				}
				else {
					result.Empty();
					return m_LastErrorType == seTimeOut ? SOCKETRECV_TIMEOUT : SOCKETRECV_ERROR;
				}
			}
			else if (m_RecvCount == 0) {
				if (!m_bUseTerminator && !result.IsEmpty()) {
					return SOCKETRECV_OK; // It's closed really, but we have data
				}
				else {
					result.Empty();
					return SOCKETRECV_CLOSED;
				}
			}

			// If terminator1 was the last char read on the last receive
			// check here to see if we match terminator two as the first
			// char of this receive.  If so, return result.
			if (!result.IsEmpty() &&
				(m_bUseTerminator == 2) &&
				(m_RecvBuf[0] == m_Terminator2) &&
				(result[result.GetLength() - 1] == m_Terminator))
			{
				if (m_RecvCount == 1) {
					m_RecvStart = 0;	// Do actual receive next time
					m_RecvCount = 0;
				}
				else m_RecvStart = 1;	// Still more data in buffer for next call

				result = result.Left(result.GetLength() - 1);	// Crop off terminator1
				return SOCKETRECV_OK;
			}
		}

		if (!m_bUseTerminator) {
			// With no designated terminator, return any data received
			// Obvious note, but the terminator won't be turned off inside this
			// function, so we know that after processing this, we can return
			DWORD numBytes = m_RecvCount - m_RecvStart;
			TCHAR* r = result.GetBuffer(numBytes);
			#ifdef __WINCE__
				// Whether this is the right thing to do is questionable
				// Maybe we should simply pack two bytes per unicode char
				for (int xx = 0; xx < numBytes; ++xx) {
					r[xx] = (TCHAR)(m_RecvBuf[m_RecvStart + xx]);
				}
			#else
				memcpy(r, m_RecvBuf + m_RecvStart, numBytes);
			#endif
			result.ReleaseBuffer(numBytes);
			m_RecvStart = 0;
			m_RecvCount = 0;
			return SOCKETRECV_OK;
		}

		int idx;

		// Look for the terminator...
		for (idx = m_RecvStart; idx < m_RecvCount; ++idx) {
			if (m_RecvBuf[idx] == m_Terminator) {
				if (m_bUseTerminator == 2) {
					if ((idx + 1 >= m_RecvCount) || (m_RecvBuf[idx + 1] != m_Terminator2)) {
						// This isn't the real termination, only first char matched
						// Keep doing the for loop (of course for loop might be over anyway)
						continue;
					}
				}
				// Terminator found; return bytes up to but not including terminator
				DWORD numBytes = idx - m_RecvStart;// - m_bUseTerminator;
				DWORD oldLength = result.GetLength();
				TCHAR* r = result.GetBuffer(numBytes + oldLength);
				#ifdef __WINCE__
					// Whether this is the right thing to do is questionable
					// Maybe we should simply pack two bytes per unicode char
					for (int xx = 0; xx < numBytes; ++xx) {
						r[xx + oldLength] = (TCHAR)(m_RecvBuf[m_RecvStart + xx]);
					}
				#else
					memcpy(r + oldLength, m_RecvBuf + m_RecvStart, numBytes);
				#endif
				result.ReleaseBuffer(numBytes + oldLength);

				if (idx == m_RecvCount - m_bUseTerminator) {
					m_RecvStart = 0;	// Finished receive buffer, request more data
					m_RecvCount = 0;
				}
				else {
					//m_RecvStart = idx + 1;		// Start here on next call to this func
					m_RecvStart = idx + m_bUseTerminator;
				}
				return SOCKETRECV_OK;
			}
		}

		if (idx == m_RecvCount) {
			DWORD numBytes = m_RecvCount - m_RecvStart;
			DWORD oldLength = result.GetLength();
			TCHAR* r = result.GetBuffer(numBytes + oldLength);
			#ifdef __WINCE__
				// Whether this is the right thing to do is questionable
				// Maybe we should simply pack two bytes per unicode char
				for (int xx = 0; xx < numBytes; ++xx) {
					r[xx + oldLength] = (TCHAR)(m_RecvBuf[m_RecvStart + xx]);
				}
			#else
				memcpy(r + oldLength, m_RecvBuf + m_RecvStart, numBytes);
			#endif
			result.ReleaseBuffer(numBytes + oldLength);
			m_RecvStart = 0;
			m_RecvCount = 0;
		}
	}
}

/*int TSocket::Receive(TString& result)
{
	//char m_ReceiveBuf[RECVBUF_SIZE];
	//int m_ReceiveCount;
	//int m_ReceiveStart;

	result.Empty();
	while(true)	{
		if (m_RecvStart == 0) {
			m_RecvCount = TSocket::Receive(m_RecvBuf, RECVBUF_SIZE - 1);
			if (m_RecvCount == SOCKET_ERROR) {
				result.Empty();
				return m_LastErrorType == seTimeOut ? SOCKETRECV_TIMEOUT : SOCKETRECV_ERROR;
			}
			else if (m_RecvCount == 0) {
				result.Empty();
				return SOCKETRECV_CLOSED;
			}
			m_RecvBuf[m_RecvCount] = 0;

			// If terminator1 was the last char read on the last receive
			// check here to see if we match terminator two as the first
			// char of this receive.  If so, return result.
			if (!result.IsEmpty() &&
				(m_bUseTerminator == 2) &&
				(m_RecvBuf[0] == m_Terminator2) &&
				(result[result.GetLength() - 1] == m_Terminator))
			{
				if (m_RecvCount == 1) m_RecvStart = 0;	// Do actual receive next time
				else m_RecvStart = 1;	// Still more data in buffer for next call

				result = result.Left(result.GetLength() - 1);	// Crop off terminator1
				return SOCKETRECV_OK;
			}
		}

		if (!m_bUseTerminator) {
			// With no designated terminator, return any data received
			char* r = m_RecvBuf + m_RecvStart;
			m_RecvStart = 0;	// Start receiving at head of buffer next time
			result = r;
			return SOCKETRECV_OK;
		}

		int idx;

		// Look for the terminator...
		for (idx = m_RecvStart; idx < m_RecvCount; ++idx) {
			if (m_RecvBuf[idx] == m_Terminator) {
				if (m_bUseTerminator == 2) {
					if ((idx + 1 >= m_RecvCount) || (m_RecvBuf[idx + 1] != m_Terminator2)) {
						// This isn't the real termination, only first char matched
						// Keep doing the for loop (of course for loop might be over anyway)
						continue;
					}
				}

				m_RecvBuf[idx] = 0;
				result += (m_RecvBuf + m_RecvStart); // What if char is 0x00?
				if (idx == m_RecvCount - 1) {
					m_RecvStart = 0;	// Finished receive buffer, request more data
				}
				else {
					//m_RecvStart = idx + 1;		// Start here on next call to this func
					m_RecvStart = idx + m_bUseTerminator;
				}
				return SOCKETRECV_OK;
			}
		}

		if (idx == m_RecvCount) {
			m_RecvBuf[idx] = 0;
			result += (m_RecvBuf + m_RecvStart); // What if char is 0x00?
			m_RecvStart = 0;
		}
	}
}*/

/*TString TSocket::Receive()
{
	//char m_ReceiveBuf[RECVBUF_SIZE];
	//int m_ReceiveCount;
	//int m_ReceiveStart;
	TString result;

	while(true)	{
		if (m_RecvStart == 0) {
			m_RecvCount = Receive(m_RecvBuf, RECVBUF_SIZE - 1);
			if (m_RecvCount == SOCKET_ERROR) return _T("");
			m_RecvBuf[m_RecvCount] = 0;
		}

		if (!m_bUseTerminator) {
			// With no designated terminator, return any data received
			char* r = m_RecvBuf + m_RecvStart;
			m_RecvStart = 0;	// Start receiving at head of buffer next time
			return r;
		}

		int idx;

		// Look for the terminator...
		for (idx = m_RecvStart; idx < m_RecvCount; ++idx) {
			if (m_RecvBuf[idx] == m_Terminator) {
				m_RecvBuf[idx] = 0;
				result += (m_RecvBuf + m_RecvStart);
				if (idx == m_RecvCount - 1) {
					m_RecvStart = 0;	// Finished receive buffer, request more data
				}
				else {
					m_RecvStart = idx + 1;		// Start here on next call to this func
				}
				return result;
			}
		}

		if (idx == m_RecvCount) {
			m_RecvBuf[idx] = 0;
			result += (m_RecvBuf + m_RecvStart);
			m_RecvStart = 0;
		}
	}
}*/

/*TString TSocket::Receive()
{
	char buf[1024*32];
	int start,i;
	start = 0;
	while(true)
	{
		i = Receive(buf + start, 32*1024 - start);

		if (i==SOCKET_ERROR) return _T("");

		for (int k = start; k < start + i ;k++) {
			if (buf[k] == 3) {
				buf[k] = 0;
				return buf;
			}
		}
		start += i;
	}
}*/

int TSocket::Read(TString& result)
{
	// If a terminator is being used, temporarily disable it for this call
	int bUseTerminator = m_bUseTerminator;
	m_bUseTerminator = 0;

	int code = Receive(result);

	m_bUseTerminator = bUseTerminator;
	return code;
}

int TSocket::ReadLine(TString& result)
{
	// Should we throw an error if no terminator is set?
	return Receive(result);
}

void TSocket::Write(TString data)
{
	Send(data);
}

void TSocket::WriteLine(TString data)
{
	Send(data);

	/*TString term;
	if (GetTerminator(term)){
		Send(term);
	}*/
}

TString TSocket::SockErrorStr(int error)
{
	// Some day I should actually write out the meanings of these errors

#if defined(__WINNT__) || defined(__WINCE__)
	switch (error) {
		case WSANOTINITIALISED:
			return _T("Winsock error: WSANOTINITIALISED");
		case WSAENETDOWN:
			return _T("Winsock error: WSAENETDOWN");
		case WSAEADDRINUSE:
			return _T("Winsock error: WSAEADDRINUSE");
		case WSAEINTR:
			return _T("Winsock error: WSAEINTR");
		case WSAEINPROGRESS:
			return _T("Winsock error: WSAEINPROGRESS");
		case WSAEALREADY:
			return _T("Winsock error: WSAEALREADY");
		case WSAEADDRNOTAVAIL:
			return _T("Winsock error: WSAEADDRNOTAVAIL");
		case WSAEAFNOSUPPORT:
			return _T("Winsock error: WSAEAFNOSUPPORT");
		case WSAECONNREFUSED:
			return _T("Winsock error: WSAECONNREFUSED");
		case WSAEFAULT:
			return _T("Winsock error: WSAEFAULT");
		case WSAEINVAL:
			return _T("Winsock error: WSAEINVAL");
		case WSAEISCONN:
			return _T("Winsock error: WSAEISCONN");
		case WSAENETUNREACH:
			return _T("Winsock error: WSAENETUNREACH");
		case WSAENOBUFS:
			return _T("Winsock error: WSAENOBUFS");
		case WSAENOTSOCK:
			return _T("Winsock error: WSAENOTSOCK");
		case WSAETIMEDOUT:
			return _T("Winsock error: WSAETIMEDOUT");
		case WSAEWOULDBLOCK:
			return _T("Winsock error: WSAEWOULDBLOCK");
		case WSAEACCES:
			return _T("Winsock error: WSAEACCES");
		default:
			{
				TString temp;
				temp.Format(_T("Unknown winsock error: %d"), error);
				return temp;
			}
	}
#else
	switch (errno) {	// Unix uses the global errno here
		case EBADF:
			return _T("Socket error: EBADF");
		case EFAULT:
			return _T("Socket error: EFAULT");
		case ENOTSOCK:
			return _T("Socket error: ENOTSOCK");
		case EISCONN:
			return _T("Socket error: EISCONN");
		case ECONNREFUSED:
			return _T("Socket error: ECONNREFUSED");
		case ETIMEDOUT:
			return _T("Socket error: ETIMEDOUT");
		case ENETUNREACH:
			return _T("Socket error: ENETUNREACH");
		case EADDRINUSE:
			return _T("Socket error: EADDRINUSE");
		case EINPROGRESS:
			return _T("Socket error: EINPROGRESS");
		case EALREADY:
			return _T("Socket error: EALREADY");
		default:
			return _T("Socket error");
	}
#endif
}

//===========================================================================
// Ping support
#if defined(__WINNT__) || defined(__WINCE__)

#define ICMP_ECHO		8
#define ICMP_ECHOREPLY	0
#define ICMP_MIN		8	// Min of 8 byte packet

typedef struct IcmpHeader {
	U8 type;
	U8 code;
	U16 checksum;
	U16 id;
	U16 sequence;
	U32 timestamp;	// Added to normal ICMP header
} IcmpHeader_t;

#define ICMP_HEADER_SIZE	sizeof(IcmpHeader_t);

typedef struct IPHeader {
	U8	headerLength;
	U8	IPVersion;
	U8	tos;
	U16	length;
	U16	id;
	U16	fragOffset;
	U8	ttl;
	U8	protocol;
	U16	checksum;
	struct	in_addr ip_src;
	struct	in_addr ip_dst;
} IPHeader_t;

#define IP_HEADER_SIZE		sizeof(IPHeader_t);

U16 CalcPingChecksum(U16* buf, int size)
{
	U32 checksum = 0;

	while (size > 1) {
		checksum += *buf++;
		size -= sizeof(U16);
	}

	if (size != 0) {
		checksum += *(U8*)buf;
	}

	checksum = (checksum >> 16) + (checksum & 0xFFFF);
	checksum += (checksum >> 16);
	return (U16)(~checksum);
}

void FillPingICMPHeader(char* icmpData, int dataSize)
{
	IcmpHeader_t* header;
	char* data;

	header = (IcmpHeader_t*)icmpData;
	header->type = ICMP_ECHO;
	header->code = 0;
	header->id = (U16)GetCurrentProcessId();
	header->checksum = 0;
	header->sequence = 0;

	data = icmpData + sizeof(IcmpHeader_t);
	memset(data, 'Z', dataSize);	// Garbage
	int x = sizeof(IcmpHeader_t);
	x += 0;
}

DWORD TSocket::Ping(LPCTSTR hostName)
{
	if (m_bConnected) {
		//ERROR_MSG(_T("Ping error"), _T("Can't ping until current connection is closed"));
		return PING_ERROR;
	}

	if (!CreateSocket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) {
		return PING_ERROR;
	}

	_tcscpy(m_HostName, hostName);
	GenerateSockAddr();

	char sendBuf[sizeof(IcmpHeader_t) + 32];
	IcmpHeader_t* icmpHeader = (IcmpHeader_t*)sendBuf;
	FillPingICMPHeader(sendBuf, 32);
	icmpHeader->timestamp = GetTickCount();
	icmpHeader->checksum = CalcPingChecksum((U16*)icmpHeader, sizeof(IcmpHeader_t) + 32);

	//TCHAR msg[128];
	//wsprintf(msg, _T("TimeStamp sent = %d\n"), icmpHeader->timestamp);
	//OutputDebugString(msg);

	int sentCount = sendto(m_Socket, (LPSTR)icmpHeader, sizeof(IcmpHeader_t) + 32, 0,
		(PSOCKADDR)&m_sockAddr, sizeof(m_sockAddr));
	if (sentCount == SOCKET_ERROR) {
		//ERROR_MSG(_T("Ping error"), _T("Failed to send ping packet"));
		closesocket(m_Socket);
		return PING_ERROR;
	}

	struct timeval timeout;
	timeout.tv_sec = 2;
	timeout.tv_usec = 0;

	fd_set readfds;
	readfds.fd_count = 1;
	readfds.fd_array[0] = m_Socket;

	int selectResult = select(1, &readfds, NULL, NULL, &timeout);
	if ((selectResult == 0) || (selectResult == SOCKET_ERROR)) {
		//ERROR_MSG(_T("Ping error"), _T("Select failed or timed out"));
		closesocket(m_Socket);
		return PING_ERROR;
	}

	struct sockaddr fromAddr;
	int sizeAddr = sizeof(fromAddr);
	char recvBuf[4096];
	int recvCount = recvfrom(m_Socket, recvBuf, 4096, 0, &fromAddr, &sizeAddr);
	if (recvCount == SOCKET_ERROR) {
		//ERROR_MSG(_T("Ping error"), _T("Ping receive failed"));
		closesocket(m_Socket);
		return PING_ERROR;
	}

	IcmpHeader_t* h = (IcmpHeader_t*)(recvBuf + ((recvBuf[0] & 0xF) << 2));
	//_stprintf(msg, _T("TimeStamp recv = %d\n"), h->timestamp);
	//OutputDebugString(msg);

	closesocket(m_Socket);
	return GetTickCount() - h->timestamp;
}

#endif // defined(__WINNT__) || defined(__WINCE__)
